#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "Thread.hpp"

// V2: 对V1进行修改，引入封装的线程

const int N = 5;

template <typename T>
class threadPool
{
public:
    // num: 线程数目
    threadPool(int num = N) : _num(num) /*, _pts(num) */
    { 
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    void init()
    {
        // 完成线程对象的创建工作
        for (int i = 0; i < _num; i++)
        {
            // 创建线程要回调 threadRoutine,参数是this threadRoutine的参数需要this
            _pts.push_back(Thread(i + 1, threadRoutine, this));
        }
    }

    // 线程的入口函数，如果放在类内的话，必须是静态成员函数，否则参数会不匹配。
    // 如果线程的入口函数要访问类内的属性的话，args只能传入this指针
    // 这里的线程是充当消费者的，让它从任务队列中获取任务，并执行
    static void /* * */ threadRoutine(void *args)
    {
        pthread_detach(pthread_self()); // 进行分离，不在等待线程
        threadPool<T> *tp = static_cast<threadPool<T> *>(args);
        while (true)
        {
            // 1.获取任务
            Task t;
            tp->popTask(&t); // 你也可以将锁拿放在这里，不放在popTask内部，不过有个问题放在这里，
                             // 任务处理在临界区内吗？不在，它必须是并发处理的，内部并不存在临界资源
            // 2.处理任务
            t();
            cout << "consumer get a task: " << t.formatExpression() << t.formatResult() << endl;
        }
    }

    void start()
    {
        // for (int i = 0; i < _num; i++)
        // {
        //     pthread_create(&_pts[i], nullptr, threadRoutine, this);
        // }
        for(auto& t : _pts)
            t.run();

    }
    void check()
    {
        for(auto & t : _pts)
        {
            cout << t.getName() << " " << t.getStatus() << "...." << endl;
        }
    }

    void pushTask(T &in)
    {
        lock();
        _tasks.push(in);
        wakeUp();
        unlock();
    }

    void popTask(T *out)
    {
        lock();
        while (isEmpty())
            wait();
        *out = _tasks.front(); // 将公共区域的任务拿到自己的独立栈上，因此才能够并发的执行任务
        _tasks.pop();
        unlock();
    }

    ~threadPool()
    {
        for(auto& t : _pts)
            t.join();
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    bool isEmpty()
    {
        return _tasks.empty();
    }
    void lock()
    {
        pthread_mutex_lock(&_mutex);
    }
    void unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void wait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void wakeUp()
    {
        pthread_cond_signal(&_cond);
    }

private:
    // std::vector<pthread_t> _pts; // 对创建的线程进行管理,这里先用pthread_t代替线程
    std::vector<Thread> _pts; // 使用线程对象来完成管理

    int _num;             // 线程的数目
    std::queue<T> _tasks; // 对线程执行的任务，做管理

    // 未来任务队列一定是被多线程并发访问的，这也是目的。
    // 那么我们一定要维护要任务队列在并发访问时的安全性
    // 锁是一定要的，当任务队列为空的时候，那我线程要干什么？肯定是阻塞等待，等待任务。
    // 所以一定得有一个条件变量去判断条件
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};
